﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using Commandparser.Exceptions;
using Commandparser.Types;
using Microsoft.VisualBasic;

namespace Commandparser
{
    public class Parser
    {

        public Dictionary<string, string> Meta { get; set; }
        public Dictionary<string, Statement> Statements { get; set; }
        public List<Command> Commands { get; set; }
        public List<IPropertyObject> Types;

        public Parser()
        {
            // initialize propertys
            Meta = new Dictionary<string, string>();
            Statements = new Dictionary<string, Statement>();
            Commands = new List<Command>();
            Types = new List<IPropertyObject>();

            // add metadata
            Meta.Add("author", null);
            Meta.Add("name", null);
            Meta.Add("version", null);

            // add statements
            Statements.Add("version", null);

            // add standard types
            Types.Add(new @array());
            Types.Add(new @decimal());
            Types.Add(new @integer());
            Types.Add(new @string());
            Types.Add(new @Converter());

        }

      //  [DebuggerStepThrough()]
        public void Parse(string s)
        {
            if(s=="")
            {
                throw new SyntaxError("Syntax error: No Source given");
            }

            // replace disturbing chars
            string src = s.Replace("\n", "");
            src = src.Replace("\t", "");
            src = src.Replace(" ", "");
            src = Regex.Replace(src, "\\/\\*.+\\*\\/", ""); // remove comments

#region "Metadata"
            string[] metas = src.Split(Convert.ToChar(";"));

            foreach (var meta in metas)
            {
                if (meta.StartsWith("author"))
                {
                    Meta["author"] = meta.Split(Convert.ToChar(":"))[1];
                }
                if (meta.StartsWith("name"))
                {
                    Meta["name"] = meta.Split(Convert.ToChar(":"))[1];
                }
                if (meta.StartsWith("version"))
                {
                    Meta["version"] = meta.Split(Convert.ToChar(":"))[1];
                }

          
                if (meta.StartsWith("@"))
                {
                    Statement st = new Statement(meta);

                    if (Statements.ContainsKey(st.Name))
                    {
                        Statements[st.Name] = st;
                    }
                    else
                    {
                        throw new FatalError("Statement '" + st.Name + "' not registered");
                    }
                }

                if (Meta["author"] != null && Meta["name"] != null && Meta["version"] != null)
                {
                    break;
                }
            }
#endregion

            // transform source
            string[] cmdss = src.Split(Convert.ToChar("{"));
            src = src.Remove(0, cmdss[0].Length+1);
            src = src.Remove(src.Length-2, 2);
            src = src.Replace("}", "\n");

            //parsing commands
            foreach (var cm in src.Split(Convert.ToChar("\n")))
            {
                var cmd = new Command {Name = cm.Split(Convert.ToChar("{"))[0]};

                string props = cm.Split(Convert.ToChar("{"))[1];

                foreach (var p in props.Split(Convert.ToChar(";")))
                {
                    if (p != "")
                    {
                        string pname = p.Split(':')[0];
                        string pvalue = p.Split(':')[1];
                        IPropertyObject valuetype = new @null();

                        //check data type of pvalue
                        foreach (var ty in Types)
                        {
                            if (ty.IsType(pvalue))
                            {
                                valuetype = ty.Parse(pvalue);
                            }
                        }


                        if (Is<@null>(valuetype))
                        {
                            if (new @null().IsType(pvalue))
                            {
                                valuetype = new @null().Parse(pvalue);
                            }
                            else
                            {
                                throw new FatalError("Fatal error: unknown type of value");
                            }
                        }

                        
                        cmd.Propertys.Add(pname, valuetype);
                       
                    
                    }
                }

                
                Commands.Add(cmd);
            }

        }

        private static bool Is<t>(object input)
        {
            return object.ReferenceEquals(input.GetType(), typeof(t));
        }
    }
}
